﻿// The MIT License (MIT)
// Copyright (c) 2013 Stylianos Dritsas
//
// Permission is hereby granted, free of charge, to any person obtaining a 
// copy of this software and associated documentation files (the "Software"), 
// to deal in the Software without restriction, including without limitation 
// the rights to use, copy, modify, merge, publish, distribute, sublicense, 
// and/or sell copies of the Software, and to permit persons to whom the 
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included 
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
// DEALINGS IN THE SOFTWARE.

#region -- References ---------------------------------------------------------

//-- System Imports
//--
using System;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;
using System.Collections.Generic;

//-- Library Imports
//--
using Bentley.Geometry;
using Bentley.GenerativeComponents;
using Bentley.GenerativeComponents.Features;
using Bentley.GenerativeComponents.MicroStation;
using Bentley.GenerativeComponents.GCScript;
using Bentley.Interop.MicroStationDGN;

#endregion

#region -- Assembly Information -----------------------------------------------

[assembly: AssemblyTitle( "GcSg" )]
[assembly: AssemblyDescription( "Smart Geometry Generative Components Template" )]
[assembly: AssemblyConfiguration( "" )]
[assembly: AssemblyCompany( "" )]
[assembly: AssemblyProduct( "GcSg" )]
[assembly: AssemblyCopyright( "Copyright © 2013 by Stylianos Dritsas" )]
[assembly: AssemblyTrademark( "" )]
[assembly: AssemblyCulture( "" )]
[assembly: ComVisible( false )]
[assembly: Guid( "118b6a17-2b0c-4fe4-ad90-033952810c4c" )]
[assembly: AssemblyVersion( "1.0.0.0" )]
[assembly: AssemblyFileVersion( "1.0.0.0" )]

#endregion

namespace Sutd
{
  public class ManagedFeature : Feature
  {
    #region -- MDL Imports ----------------------------------------------------

    private const string LIBRARY_BUILT_IN = "stdmdlbltin.dll";
    private const string LIBRARY_KISOLID  = "kisolid.dll";

    #region -- Prototypes -----------------------------------------------------

    [DllImport( LIBRARY_BUILT_IN )]
    protected static extern int mdlModelRef_getActive( );

    [DllImport( LIBRARY_BUILT_IN )]
    protected static extern int mdlElmdscr_deleteByModelRef(
      int descriptor,  
      int position,
      int model,
      int display
    );

    [DllImport( LIBRARY_BUILT_IN )]
    protected static extern int mdlElmdscr_rewriteByModelRef(
      int new_descriptor,
      int old_descriptor,
      int position,
      int model
    );

    [DllImport( LIBRARY_BUILT_IN )]
    protected unsafe static extern int mdlLine_create(
      ref MDL_ELEMENT element,
                  int template,
             double[] points
    );

    [DllImport( LIBRARY_BUILT_IN )]
    protected static extern int mdlElement_add(
      ref MDL_ELEMENT element
    );

    [DllImport( LIBRARY_BUILT_IN )]
    protected static extern int mdlElement_read(
      ref MDL_ELEMENT element,
                  int model,
                  int position
    );

    [DllImport( LIBRARY_BUILT_IN )]
    protected static extern void mdlElement_setSymbology(
      ref MDL_ELEMENT element,
              ref int color,
              ref int weight,
              ref int style
    );

    [DllImport( LIBRARY_BUILT_IN )]
    protected static extern int mdlElmdscr_transform(
      int descriptor,
      ref MDL_TRANSFORM transform
    );

    [DllImport( LIBRARY_BUILT_IN )]
    protected static extern int mdlElmdscr_add(
      int descriptor
    );

    [DllImport( LIBRARY_BUILT_IN )]
    protected static extern int mdlElmdscr_duplicate(
      ref int new_descriptor,
          int old_descriptor
    );

    [DllImport( LIBRARY_BUILT_IN )]
    protected static extern int mdlElmdscr_read(
      ref int descriptor,
          int position,
          int model,
          int expand,
          int read          
    );

    [DllImport( LIBRARY_BUILT_IN )]
    protected static extern int mdlView_updateSingle(
      int view
    );

    [DllImport( LIBRARY_BUILT_IN )]
    protected static extern int mdlView_getMaxCount( );

    [DllImport( LIBRARY_BUILT_IN )]
    protected static extern int mdlElement_rewrite(
      ref MDL_ELEMENT new_element,
      ref MDL_ELEMENT old_element,
                  int position
    );

    [DllImport( LIBRARY_BUILT_IN )]
    protected static extern int mdlCnv_masterToUOR(
      double[] numbers,
      double units,
      int model
    );

    [DllImport( LIBRARY_BUILT_IN )]
    protected static extern int mdlCnv_masterToUOR(
      ref double number,
          double units,
          int model
    );

    [DllImport( LIBRARY_BUILT_IN )]
    protected static extern double mdlCnv_masterUnitsToUors(
      double number
    );

    [DllImport( LIBRARY_BUILT_IN )]
    protected static extern void mdlElmdscr_new(
      ref int descriptor,
          int parent,
          int element
    );

    [DllImport( LIBRARY_BUILT_IN )]
    protected static extern void mdlElmdscr_freeAll(
      ref int descriptor
    );

    [DllImport( LIBRARY_KISOLID )]
    protected static extern int mdlKISolid_beginCurrTrans(
      int model
    );

    [DllImport( LIBRARY_KISOLID )]
    protected static extern void mdlKISolid_endCurrTrans( );

    [DllImport( LIBRARY_KISOLID )]
    protected static extern int mdlKISolid_makeCuboid( 
      ref int body, 
       double dx, 
       double dy, 
       double dz 
    );

    [DllImport( LIBRARY_KISOLID )]
    protected static extern int mdlKISolid_bodyToElement(
      ref int descriptor,
          int body,
          int wireframe,
          int isoparams,
          int template,
          int model
    );

    [DllImport( LIBRARY_KISOLID )]
    protected static extern int mdlKISolid_bodyToElementD(
      ref int descriptor,
          int body,
          int wireframe,
          int isoparams,
          int template,
          int model,
          int spatial
    );

    [DllImport( LIBRARY_KISOLID )]
    protected static extern void mdlKISolid_freeBody(
      int body
    );

    [DllImport( LIBRARY_KISOLID )]
    protected static extern double mdlKISolid_getScale(
      int model
    );

    #endregion

    #region -- Unions ---------------------------------------------------------

    [StructLayout( LayoutKind.Explicit, Size = 24 )]
    protected struct MDL_DPOINT3D {
      [FieldOffset(  0 )] public double x;
      [FieldOffset(  8 )] public double y;
      [FieldOffset( 16 )] public double z;

      public static MDL_DPOINT3D X = new MDL_DPOINT3D( 1.0, 0.0, 0.0 );
      public static MDL_DPOINT3D Y = new MDL_DPOINT3D( 0.0, 1.0, 0.0 );
      public static MDL_DPOINT3D Z = new MDL_DPOINT3D( 0.0, 0.0, 1.0 );

      public MDL_DPOINT3D( double x, double y, double z ) {
        this.x = x;
        this.y = y;
        this.z = z;
      }
    }

    [StructLayout( LayoutKind.Explicit, Size = 32 )]
    protected struct MDL_DPOINT4D {
      [FieldOffset(  0 )] public double x;
      [FieldOffset(  8 )] public double y;
      [FieldOffset( 16 )] public double z;
      [FieldOffset( 24 )] public double w;
    }

    [StructLayout( LayoutKind.Explicit, Size = 72 )]
    protected struct MDL_DMATRIX3D {
      [FieldOffset(  0 )] public MDL_DPOINT3D x;
      [FieldOffset( 24 )] public MDL_DPOINT3D y;
      [FieldOffset( 48 )] public MDL_DPOINT3D z;
    }

    [StructLayout( LayoutKind.Explicit, Size = 86 )]
    protected struct MDL_TRANSFORM {
      [FieldOffset(  0 )] public MDL_DPOINT4D x;
      [FieldOffset( 32 )] public MDL_DPOINT4D y;
      [FieldOffset( 64 )] public MDL_DPOINT4D z;

      public MDL_TRANSFORM(
        double xx, double xy, double xz,
        double yx, double yy, double yz,
        double zx, double zy, double zz,
        double tx, double ty, double tz
      ) {
        x.x = xx; x.y = xy; x.z = xz; x.w = tx;
        y.x = yx; y.y = yy; y.z = yz; y.w = ty;
        z.x = zx; z.y = zy; z.z = zz; z.w = tz;
      }
    }

    [StructLayout( LayoutKind.Explicit, Size = 12 )]
    protected struct MDL_SYMBOLOGY {
      [FieldOffset( 0 )] public int  style;
      [FieldOffset( 4 )] public uint weight;
      [FieldOffset( 8 )] public uint color;
    }

    [StructLayout( LayoutKind.Explicit, Size = 32 )]
    protected struct MDL_ELEMENT_HEADER {
      [FieldOffset(  0 )] public ushort type;
      [FieldOffset(  2 )] public ushort flags;
      [FieldOffset(  4 )] public uint   size;
      [FieldOffset(  8 )] public uint   offset;
      [FieldOffset( 12 )] public uint   level;
      [FieldOffset( 16 )] public ulong  id;
      [FieldOffset( 24 )] public double	modified;
    }

    [StructLayout( LayoutKind.Explicit, Size = 104 )]
    protected struct MDL_HEADER {
      [FieldOffset(  0 )] public MDL_ELEMENT_HEADER element;
      [FieldOffset( 32 )] public MDL_DISPLAY_HEADER display;
    }

    [StructLayout( LayoutKind.Explicit, Size = 48 )]
    protected struct MDL_SCAN_RANGE {
      [FieldOffset(  0 )] public long xlowlim;
      [FieldOffset(  8 )] public long ylowlim;
      [FieldOffset( 16 )] public long zlowlim;
      [FieldOffset( 24 )] public long xhighlim;
      [FieldOffset( 32 )] public long yhighlim;
      [FieldOffset( 40 )] public long zhighlim;
    }

    [StructLayout( LayoutKind.Explicit, Size = 72 )]
    protected struct MDL_DISPLAY_HEADER {
      [FieldOffset(  0 )] public uint           graphic_group;
      [FieldOffset(  4 )] public uint           priority;
	    [FieldOffset(  8 )] public ushort         properties;
      [FieldOffset( 10 )] public ushort         reserved;
      [FieldOffset( 12 )] public MDL_SYMBOLOGY  symbology;
      [FieldOffset( 24 )] public MDL_SCAN_RANGE range;
    }

    [StructLayout( LayoutKind.Sequential )]
    protected struct MDL_CELL_3D {
      public MDL_ELEMENT_HEADER element;
      public MDL_DISPLAY_HEADER display;
      public uint	              components;
      public ushort             class_map;
      public ushort	            reserved;
      public MDL_DPOINT3D	      low;
      public MDL_DPOINT3D       high;
      public MDL_DMATRIX3D      transform;
      public MDL_DPOINT3D	      origin;
    }

    [StructLayout( LayoutKind.Sequential )]
    protected struct MDL_LINE_3D {
      public MDL_ELEMENT_HEADER element;
      public MDL_DISPLAY_HEADER display;
      public MDL_DPOINT3D	      start;
      public MDL_DPOINT3D	      end;
    }

    [StructLayout( LayoutKind.Explicit )]
    protected struct MDL_ELEMENT {
      [FieldOffset( 0 )] public MDL_ELEMENT_HEADER element;
      [FieldOffset( 0 )] public MDL_DISPLAY_HEADER display;
      [FieldOffset( 0 )] public MDL_CELL_3D        cell3d;
      [FieldOffset( 0 )] public MDL_LINE_3D        line3d;
    }

    #endregion

    protected static MDL_ELEMENT element;            //-- Receive/Transmit MDL elements
    protected static double[]    points;             //-- Avoid allocation of points and matrices
    protected static double      solid_units;        //-- Parasolid unit conversion factor
    protected static double      model_units;        //-- Microstation unit conversion factor

    #endregion

    #region -- Management -----------------------------------------------------

    //-- Garbage Collection
    //--
    protected List<int> geometry;

    //-- Initialize Units etc
    //--
    public ManagedFeature( ) : base( )
    {
      model_units = mdlCnv_masterUnitsToUors( 1.0 );
      solid_units = model_units / mdlKISolid_getScale( mdlModelRef_getActive( ) );
      points      = new double[6];
      geometry    = new List<int>( );
    }

    //-- Perform Garbage Collection
    //--
    protected void Regenerate( )
    {
      int model = mdlModelRef_getActive( );
      foreach( var element in geometry )
      {
        mdlElmdscr_deleteByModelRef( 0, element, model, 0 );
      }
      geometry.Clear( );
    }

    //-- Clean up before exiting
    //--
    protected override void OnBeingDeleted( )
    {
      Regenerate( );
      base.OnBeingDeleted( );
    }

    #endregion

    #region -- Drawing --------------------------------------------------------

    //-- Tutorial of how to use the MDL interface to communicate with
    //-- Microstation kernel modules. MDL calls are much faster than 
    //-- those provided through the COM interface.
    //--
    public void AddPoint( Point3d p, int color = 1, int weight = 8, int style = 0 )
    {
      AddLine( p, p, color, weight, style );
    }

    public void AddLine( Point3d pa, Point3d pb, int color = 0, int weight = 0, int style = 0 )
    {
      points[0] = pa.X; points[1] = pa.Y; points[2] = pa.Z;
      points[3] = pb.X; points[4] = pb.Y; points[5] = pb.Z;
      if( mdlLine_create( ref element, 0, points ) == 0 )
      {
        mdlElement_setSymbology( ref element, ref color, ref weight, ref style );
        var position = mdlElement_add( ref element );
        if( position != 0 ) geometry.Add( position );
      }
    }

    //-- Tutorial of how to use the Parasolid kernel to
    //-- draw brep geometries that are impossible through
    //-- the COM interface.
    //--
    public void AddBox( DTransform3d m, DVector3d s )
    {      
      int position = 0;

      int model = mdlModelRef_getActive( );

      mdlKISolid_beginCurrTrans( model );

      var body = 0;
      var result = mdlKISolid_makeCuboid( ref body, 
        s.x * solid_units,
        s.y * solid_units,
        s.z * solid_units );
      if( result == 0 ) 
      {
        int descriptor = 0;
        result = mdlKISolid_bodyToElementD(  ref descriptor, body, 1, -1, 0, model, 1 );
        if( result == 0 ) 
        {          
          var transform = new MDL_TRANSFORM(
            m.RowX.x, m.RowX.y, m.RowX.z,
            m.RowY.x, m.RowY.y, m.RowY.z,
            m.RowZ.x, m.RowZ.y, m.RowZ.z,
            m.Translation.x, 
            m.Translation.y, 
            m.Translation.z
          );

          mdlElmdscr_transform( descriptor, ref transform );
          position = mdlElmdscr_add( descriptor );

          mdlElmdscr_freeAll( ref descriptor );
          geometry.Add( position );
        }
        mdlKISolid_freeBody( body );
      }
      mdlKISolid_endCurrTrans( );
    }

    #endregion
  }
}
